/***************************************************************
 * Copyright(C) 2024, HTDisplay. All Rights Reserved
 *
 * This file is part of the HtDisplaySDK Project.
 *
 * @file    :  GpioController.cpp
 *
 * @brief   :  GPIO类
 *
 * @author  :  郑满乐
 *
 * @date    :  2024-11-19
 ***************************************************************/

#include "GpioController.h"
#include <iostream>
#include <fstream>
#include <unistd.h>
#include <regex>

GpioController::GpioController(std::string name) {
    std::string resolvedPath = resolveSymlink(name);
    if (resolvedPath.empty()) {
        gpioNumber = -1;
        gpioDirection = "";
        return;
    }

    gpioNumber = extractGpioNumber(resolvedPath);
    if (gpioNumber == -1) {
        gpioDirection = "";
    }

    gpioDirection = extractGpioDirection(name);
    if (gpioDirection.empty()) {
        gpioNumber = -1;
        return;
    }
}

GpioController::GpioController(int number, const std::string& direction)
    : gpioNumber(number), gpioDirection(direction) {

}

GpioController::~GpioController() {

}

bool GpioController::fileExists(const std::string& path) {
    return access(path.c_str(), F_OK) == 0;
}

bool GpioController::writeToFile(const std::string& path, const std::string& content) {
    std::ofstream file(path);
    if (!file.is_open()) {
        std::cerr << "Failed to open file for writing: " << path << std::endl;
        return false;
    }
    file << content;
    file.close();
    return true;
}

bool GpioController::readFromFile(const std::string& path, std::string& content) {
    std::ifstream file(path);
    if (!file.is_open()) {
        std::cerr << "Failed to open file for reading: " << path << std::endl;
        return false;
    }
    file >> content;
    file.close();
    return true;
}

std::string GpioController::resolveSymlink(const std::string& path) {
    char buffer[256];
    ssize_t len = readlink(path.c_str(), buffer, sizeof(buffer) - 1);
    if (len == -1) {
        std::cerr << "Failed to resolve symlink: " << path
                  << " Error: " << strerror(errno) << std::endl;
        return "";
    }
    buffer[len] = '\0';
    return std::string(buffer);
}

int GpioController::extractGpioNumber(const std::string& path) {
    std::regex gpioRegex("gpio(\\d+)");
    std::smatch match;
    if (std::regex_search(path, match, gpioRegex)) {
        return std::stoi(match[1]);
    } else {
        std::cerr << "Failed to extract GPIO number from path: " << path << std::endl;
        return -1;
    }
}

std::string GpioController::extractGpioDirection(const std::string& name) {
    if (name.find("/dev/gpio/DI-") != std::string::npos) {
        return "in";
    } else if (name.find("/dev/gpio/DO-") != std::string::npos) {
        return "out";
    } else {
        std::cerr << "Invalid name format: " << name << std::endl;
        return "";
    }
}

bool GpioController::init() {
    if (gpioDirection != "in" && gpioDirection != "out") {
        std::cerr << "Invalid direction: " << gpioDirection << std::endl;
        return false;
    }

    std::string gpioPath = "/sys/class/gpio/gpio" + std::to_string(gpioNumber);

    if (!fileExists(gpioPath)) {
        if (!writeToFile("/sys/class/gpio/export", std::to_string(gpioNumber))) {
            std::cerr << "Failed to export GPIO: " << gpioNumber << std::endl;
            return false;
        }
    }

    std::string directionPath = gpioPath + "/direction";
    std::string currentDirection;
    if (readFromFile(directionPath, currentDirection)) {
        if (currentDirection != gpioDirection) {
            if (!writeToFile(directionPath, gpioDirection)) {
                std::cerr << "Failed to set GPIO direction: " << directionPath << std::endl;
                return false;
            }
        }
    } else {
        std::cerr << "Failed to read GPIO direction: " << directionPath << std::endl;
        return false;
    }

    return true;
}

bool GpioController::setValue(int value) {
    std::string valuePath = "/sys/class/gpio/gpio" + std::to_string(gpioNumber) + "/value";
    if (!writeToFile(valuePath, std::to_string(value))) {
        std::cerr << "Failed to set GPIO value: " << valuePath << std::endl;
        return false;
    }
    return true;
}

bool GpioController::getValue(int& value) {
    std::string valuePath = "/sys/class/gpio/gpio" + std::to_string(gpioNumber) + "/value";
    std::string content;
    if (!readFromFile(valuePath, content)) {
        std::cerr << "Failed to get GPIO value: " << valuePath << std::endl;
        return false;
    }
    value = std::stoi(content);
    return true;
}
